package com.erik.crawler.controller;

import com.erik.crawler.service.crawler.CrawlerJsoupImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Semaphore;

@Controller
public class RestUploadController {
    private static final Semaphore semaphore = new Semaphore(20, true);//default 20
    private final Logger logger = LoggerFactory.getLogger(RestUploadController.class);
    @Autowired
    private CrawlerJsoupImpl crawlerJsoupImpl;

    /**
     * default jump upload.html
     *
     * @return
     */
    @RequestMapping("/")
    public String index() {
        return "upload";
    }

    /**
     * upload and  determine whether a search term exists on the page
     *
     * @param uploadfile
     * @return
     */
    @PostMapping("/api/upload")
    public ResponseEntity<?> resolveUploadFile(@RequestParam("files") MultipartFile uploadfile) {
        logger.debug("Single file upload!");
        if (uploadfile.isEmpty()) {
            return new ResponseEntity("please select a file!", HttpStatus.OK);
        }

        try {
            resolveUploadedFiles(Arrays.asList(uploadfile));
        } catch (IOException e) {
            return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
        }

        return new ResponseEntity("Successfully uploaded - " +
                uploadfile.getOriginalFilename(), new HttpHeaders(), HttpStatus.OK);

    }

    /**
     * Analyze file and judge
     *
     * @param files
     * @throws IOException
     */
    private void resolveUploadedFiles(List<MultipartFile> files) throws IOException {
        for (MultipartFile file : files) {
            if (file.isEmpty()) {
                continue; //next pls
            }

            //get url of txt
            List<String> urlList = new ArrayList<>();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(file.getInputStream()));
            String lineTxt;
            bufferedReader.readLine();//jump first title
            while ((lineTxt = bufferedReader.readLine()) != null) {
                if (!lineTxt.equals("")) {//In case there’s a blank line in the file
                    //1,"facebook.com/",9616487,1688316928,9.54,9.34
                    String[] urls = lineTxt.split(",");
                    if (urls.length > 0) {
                        urlList.add("http://" + lineTxt.split(",")[1].replace("\"", ""));
                    }
                }
            }

            //file prepare
            //The path should be configurable instead
            File upload = new File("D:/result");
            if (!upload.exists()) {
                upload.mkdirs();
            }

            String path = upload + File.separator + "result.txt";
            for (String url : urlList) {
                semaphoreControler(url, path, crawlerJsoupImpl);
            }

        }
    }

    /**
     * Java semaphores, which control the number of HTTP requests
     *
     * @param url
     * @param path
     * @param crawlerJsoupImpl
     */
    private static void semaphoreControler(String url, String path, CrawlerJsoupImpl crawlerJsoupImpl) {
        StringBuffer existsUrls = new StringBuffer();//exists search url
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    semaphore.acquire();
                    boolean isHasSearch = crawlerJsoupImpl.isExistsSearch(url);
                    if (isHasSearch) {
                        System.out.printf("url", url);
                        existsUrls.append(url);
                        existsUrls.append("\r\n");

                        writeFileByChannel(path, existsUrls.toString());
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                }
            }
        }).start();
    }

    /**
     * Javanio non-blocking read and write,
     * through the lock mechanism to complete the high concurrent file operation
     *
     * @param path
     * @param url
     */
    private static void writeFileByChannel(String path, String url) {
        File file = new File(path);
        RandomAccessFile fout = null;
        FileChannel fcout = null;
        try {
            fout = new RandomAccessFile(file, "rw");
            long filelength = fout.length();
            fout.seek(filelength);//Position the read and write pointer of the file to the end of the file
            fcout = fout.getChannel();//open channel
            FileLock flout = null;
            while (true) {
                try {
                    flout = fcout.tryLock();//Constantly request lock, if not, wait a second to request
                    break;
                } catch (Exception e) {
                    System.out.print("lock is exist ......");
                    Thread.sleep(1000);
                }
            }

            fout.write(url.getBytes());//write url to file

            flout.release();
            fcout.close();
            fout.close();

        } catch (IOException e1) {
            e1.printStackTrace();
            System.out.print("file no find ...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (fcout != null) {
                try {
                    fcout.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    fcout = null;
                }
            }
            if (fout != null) {
                try {
                    fout.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    fout = null;
                }
            }
        }
    }

}
